package com.wang.lotterysystem.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.wang.lotterysystem.common.exception.ServiceException;
import com.wang.lotterysystem.common.utils.RedisUtil;
import com.wang.lotterysystem.controller.enums.ActivityPrizeStatusEnum;
import com.wang.lotterysystem.controller.enums.ActivityPrizeTiersEnum;
import com.wang.lotterysystem.controller.enums.ActivityStatusEnum;
import com.wang.lotterysystem.controller.enums.ActivityUserStatusEnum;
import com.wang.lotterysystem.controller.param.CreateActivityParam;
import com.wang.lotterysystem.controller.param.PageListParam;
import com.wang.lotterysystem.dao.dataobject.activity.ActivityDO;
import com.wang.lotterysystem.dao.dataobject.activity.ActivityPrizeDO;
import com.wang.lotterysystem.dao.dataobject.activity.ActivityUserDO;
import com.wang.lotterysystem.dao.dataobject.prize.PrizeDO;
import com.wang.lotterysystem.dao.mapper.*;
import com.wang.lotterysystem.service.ActivityService;
import com.wang.lotterysystem.service.dto.activity.ActivityDTO;
import com.wang.lotterysystem.service.dto.activity.ActivityDetailDTO;
import com.wang.lotterysystem.service.dto.activity.CreateActivityDTO;
import com.wang.lotterysystem.service.dto.PageListDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class ActivityServiceImpl implements ActivityService {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    public static final String ACTIVITY_PREFIX = "ACTIVITY_"; //缓存中活动详情的前缀

    public static final long ACTIVITY_EFFECTIVE_TIME = 60*60*24*3; //过期时间

    @Override
    @Transactional(rollbackFor = Exception.class)
    //Spring事务默认只对非受查异常进行回滚,加上这个配置表示Spring自动对所有异常进行回滚
    public CreateActivityDTO createActivity(CreateActivityParam param) throws JsonProcessingException {

        //对传入的参数进行校验
        checkActivityInfo(param);

        /*
        (1)创建活动并入库
         */

        ActivityDO activityDO = new ActivityDO(); //创建活动对象
        activityDO.setActivityName(param.getActivityName()); //设置活动名称
        activityDO.setDescription(param.getDescription()); //设置活动描述
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name()); //设置活动状态

        //将创建好的活动对象入库
        activityMapper.insert(activityDO);

        /*
        (2)创建活动奖品并入库
         */

        //activityPrizeDOList
        List<ActivityPrizeDO> activityPrizeDOList = new ArrayList<>();

        //activityPrizeList
        List<CreateActivityParam.ActivityPrize> activityPrizeList = param.getActivityPrizeList();

        //遍历奖品列表中的每一个奖品并入库
        for (CreateActivityParam.ActivityPrize activityPrize : activityPrizeList) {

            //创建一个ActivityPrizeDO对象
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();

            //对activityPrizeDO的属性赋值
            activityPrizeDO.setActivityId(activityDO.getId()); //设置活动id
            activityPrizeDO.setPrizeId(activityPrize.getPrizeId()); //设置prizeId
            activityPrizeDO.setPrizeAmount(activityPrize.getPrizeAmount()); //设置奖品数量
            activityPrizeDO.setPrizeTiers(activityPrize.getPrizeTiers()); //设置奖品等级
            activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name()); //设置奖品状态
            activityPrizeDOList.add(activityPrizeDO); //将设置好的奖品DO添加进DOList中
        }
        //将奖品列表入库
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        /*
       (3)创建活动用户并入库
         */

        //activityUserDOList
        List<ActivityUserDO> activityUserDOList = new ArrayList<>();

        //activityUserList
        List<CreateActivityParam.ActivityUser> activityUserList = param.getActivityUserList();

        //遍历人员列表中的每一个人员并入库
        for (CreateActivityParam.ActivityUser activityUser : activityUserList) {

            //创建一个ActivityUserDO对象
            ActivityUserDO activityUserDO = new ActivityUserDO();

            //将activityUser中的属性设置到activityUserDO中
            activityUserDO.setActivityId(activityDO.getId()); //设置id
            activityUserDO.setUserId(activityUser.getUserId()); //设置userid
            activityUserDO.setUserName(activityUser.getUserName()); //设置人员的用户名
            activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name()); //设置人员状态
            activityUserDOList.add(activityUserDO); //将设置好的奖品DO添加进DOList中
        }
        //将人员列表入库
        activityUserMapper.batchInsert(activityUserDOList);

        //整合活动整体信息,存储在redis中.

        //获取活动中的奖品id列表
        List<Long> prizeIdList = new ArrayList<>();
        for (ActivityPrizeDO activityPrizeDO :activityPrizeDOList) {
            prizeIdList.add(activityPrizeDO.getPrizeId());
        }

        //根据奖品id从库中查出相应的奖品信息
        List<PrizeDO> prizeDOList = prizeMapper.selectByIdList(prizeIdList);
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(
                activityDO,
                prizeDOList,
                activityPrizeDOList,
                activityUserDOList
        );

        //将活动详情存入缓存
        cacheActivity(activityDetailDTO);

        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageListParam param) {
        //查询活动总数
        int count = activityMapper.count();
        //查询当前页的活动信息
        List<ActivityDO> activityDOList = activityMapper.queryActivitiesByPage(param.offset(),param.getPageSize());
        System.out.println(activityDOList);

        //将活动信息DO转换为活动信息DTO
        List<ActivityDTO> activityDTOList = new ArrayList<>();
        for (ActivityDO activityDO : activityDOList) {
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setActivityId(activityDO.getId());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setDescription(activityDO.getDescription());
            activityDTO.setStatus(ActivityStatusEnum.checkForName(activityDO.getStatus()));

            activityDTOList.add(activityDTO);
        }

        //创建一个页列表对象,设置数据总数, 设置数据内容(活动信息)
        PageListDTO<ActivityDTO> pageListDTO = new PageListDTO<>();
        pageListDTO.setTotal(count);
        pageListDTO.setRecords(activityDTOList);

        System.out.println(pageListDTO);

        return pageListDTO;
    }

    //在更改活动相关信息之后,如果ActivityId和原来一致,redis会把之前的旧数据覆盖掉
    @Override
    public void cacheActivity(Long activityId) throws JsonProcessingException {
        if (activityId == null){
            log.warn("要缓存的活动id为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //首先从数据库中获取已经更改好的活动信息
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if (activityDO == null){//查询失败
            log.warn("要缓存的活动id有误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);

        //获取奖品id,从奖品表中查询奖品详细信息
        List<Long> prizeIdList = new ArrayList<>();
        for (ActivityPrizeDO activityPrizeDO :activityPrizeDOList) {
            prizeIdList.add(activityPrizeDO.getPrizeId());
        }
        List<PrizeDO> prizeDOList = prizeMapper.selectByIdList(prizeIdList);
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(
                activityDO,
                prizeDOList,
                activityPrizeDOList,
                activityUserDOList
        );
        cacheActivity(activityDetailDTO);
    }


    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) throws JsonProcessingException {
        if (activityId == null){
            log.warn("活动id为空");
            return null;
        }

        //从缓存中获取活动详情信息
        ActivityDetailDTO activityDetail = getActivityFromCache(activityId);
        if (activityDetail != null){
            //缓存中的活动详情不为空-->直接返回
            return activityDetail;
        }

        //如果缓存中没有,就去数据库中查
        ActivityDO activityDO = activityMapper.selectById(activityId);
        //从库中查询当前活动功能的奖品信息
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        //从库中查询当前活动的人员信息
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);

        List<Long> prizeId = new ArrayList<>();
        for (ActivityPrizeDO activityPrizeDO : activityPrizeDOList) {
            prizeId.add(activityPrizeDO.getPrizeId());
        }

        //从库中查询奖品基本信息
        List<PrizeDO> prizeDOList = prizeMapper.selectByIdList(prizeId);
        //转换为活动详细信息
        activityDetail = convertToActivityDetailDTO(activityDO,prizeDOList,activityPrizeDOList,activityUserDOList);
        //将信息存入缓存 (刷新缓存)
        cacheActivity(activityDetail);

        return activityDetail;
    }

    /**
     * 从缓存中获取活动详细信息
     * @param activityId 活动id
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) throws JsonProcessingException {
        if (activityId == null){
            log.warn("activityId参数为空");
            return null;
        }
        try {
            String detail = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(detail)){
                //如果查出的detail为空-->打印日志并返回空
                log.warn("要查询的活动信息在缓存中不存在:{}",activityId);
                return null;
            }
            //如果查出的detail不为空-->将JSON转换为ActivityDetailDTO对象并返回
            return objectMapper.readValue(detail,ActivityDetailDTO.class);
        }catch (Exception e){
            log.error("获取活动详细信息异常:{}",activityId);
            return null;
        }
    }

    /**
     * 向redis中缓存活动信息
     * @param activityDetailDTO 需要缓存的活动信息
     * @throws JsonProcessingException json异常
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) throws JsonProcessingException {
        //校验参数
        if (activityDetailDTO == null || activityDetailDTO.getActivityId() == null){
            log.warn("需要缓存的活动信息不存在");
            return;
        }
        try {
            //将要存储的信息序列化为JSON后存入缓存
            redisUtil.set(ACTIVITY_PREFIX + activityDetailDTO.getActivityId(),
                    objectMapper.writeValueAsString(activityDetailDTO),
                    ACTIVITY_EFFECTIVE_TIME);
        } catch (Exception e){
            log.error("缓存活动失败:{}",objectMapper.writeValueAsString(activityDetailDTO));
        }
    }

    /**
     * 对活动创建的参数进行校验
     * @param param 活动创建参数
     */
    private void checkActivityInfo(CreateActivityParam param) {
        //校验参数不能为空
        if (param == null){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        /*
        对奖品进行校验 (只需要拿出奖品的id进行校验即可)
         */

        //从参数中获取到活动奖品列表
        List<CreateActivityParam.ActivityPrize> activityPrizeList = param.getActivityPrizeList();
        //从活动奖品列表中获取到奖品id, 并将其存入列表中
        List<Long> prizeIdList = new ArrayList<>();
        for (CreateActivityParam.ActivityPrize activityPrize : activityPrizeList) {
            prizeIdList.add(activityPrize.getPrizeId());
        }

        //从库中查询出已经存在的奖品id
        List<Long> existedPrizeIdList =  prizeMapper.batchExistByIds(prizeIdList);
        for (Long prizdId : prizeIdList) {
            if (!existedPrizeIdList.contains(prizdId)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        }

        /*
        对人员进行校验 (只需要拿出人员的id进行校验即可)
         */

        //从参数中获取到活动人员列表
        List<CreateActivityParam.ActivityUser> activityUserList = param.getActivityUserList();
        //从活动人员列表中获取到人员id, 并将其存入列表中
        List<Long> userIdList = new ArrayList<>();
        for (CreateActivityParam.ActivityUser activityUser : activityUserList) {
            userIdList.add(activityUser.getUserId());
        }

        //从库中查询出已经存在的人员id
        List<Long> existedUserIdList = userMapper.batchExistByIds(userIdList);
        for (Long userId : userIdList) {
            if (!existedUserIdList.contains(userId)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        }


        /*
        对数量进行校验 (人员数量必须大于奖品数量)
         */
        int userAmount = param.getActivityUserList().size();
        int prizeAmount = param.getActivityPrizeList().size();
        if (userAmount < prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        /*
        校验输入的奖品等级是否存在
         */
        List<String> tierList = new ArrayList<>();
        for (CreateActivityParam.ActivityPrize activityPrize : activityPrizeList) {
            tierList.add(activityPrize.getPrizeTiers());
        }
        for (String tier : tierList) {
            if (ActivityPrizeTiersEnum.checkForName(tier) == null) {
                //如果输入的奖品等级不在ActivityPrizeTiersEnum中,说明该奖品等级不存在
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        }
    }

    /**
     * 构造详细的活动信息
     * @param activityDO 活动信息
     * @param prizeDOList 奖品列表
     * @param activityPrizeDOList 活动奖品关联列表
     * @param activityUserDOList 活动人员关联列表
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList){
        /*
        (1)设置活动信息
         */
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDescription(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.checkForName(activityDO.getStatus()));

        /*
        (2)设置奖品信息
         */
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = new ArrayList<>();
        for (ActivityPrizeDO activityPrizeDO : activityPrizeDOList) {
            //外层遍历活动关联的奖品列表
            for (PrizeDO prizeDO :prizeDOList) {
                //内层遍历奖品基本信息列表
                //如果id匹配,说明当前prizeDO和当前activityPrizeDO是同一个奖品 --> 将奖品基本信息设置进活动详细信息中
                if (activityPrizeDO.getPrizeId().equals(prizeDO.getId())){
                    //创建活动详情中的奖品详情对象prizeDTO
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    //给奖品详情对象prizeDTO赋值
                    prizeDTO.setPrizeId(prizeDO.getId());
                    prizeDTO.setName(prizeDO.getName());
                    prizeDTO.setDescription(prizeDO.getDescription());
                    prizeDTO.setPrice(prizeDO.getPrice());
                    prizeDTO.setImageUrl(prizeDO.getImageUrl());
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.checkForName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.checkForName(activityPrizeDO.getStatus()));
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());

                    //将当前奖品详情对象prizeDTO加入奖品详情列表prizeDTOList中
                    prizeDTOList.add(prizeDTO);
                }
            }
        }

        //将奖品详情列表prizeDTOList设置进活动详情列表activityDetailDTO中
        activityDetailDTO.setPrizeDTOList(prizeDTOList);


        /*
        (3)设置用户信息
         */
        List<ActivityDetailDTO.UserDTO> userDTOList = new ArrayList<>();
        for (ActivityUserDO activityUserDO : activityUserDOList) {
            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
            userDTO.setUserId(activityUserDO.getUserId());
            userDTO.setUserName(activityUserDO.getUserName());
            userDTO.setStatus(ActivityUserStatusEnum.checkForName(activityUserDO.getStatus()));
            userDTOList.add(userDTO);
        }

        activityDetailDTO.setUserDTOList(userDTOList);

        return activityDetailDTO;
    }
}